package com.example.memento.download.service;

import com.example.memento.download.config.DownloadProperties;
import com.example.memento.download.model.IpGeoInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.List;
import java.util.regex.Pattern;

/**
 * GitHub 代理服务
 * 当检测到当前位置为中国时，自动使用代理下载 GitHub 资源
 */
@Slf4j
@Service
public class GithubProxyService {
    
    private static final Pattern GITHUB_PATTERN = Pattern.compile(
        "^https?://(?:www\\.)?github\\.com/.+"
    );
    
    private static final Pattern GITHUB_API_PATTERN = Pattern.compile(
        "^https?://(?:api\\.)?github\\.com/.+"
    );
    
    private final IpGeoService ipGeoService;
    private final DownloadProperties properties;
    private final HttpClient httpClient;
    private Boolean proxyAvailable;
    
    public GithubProxyService(IpGeoService ipGeoService, DownloadProperties properties) {
        this.ipGeoService = ipGeoService;
        this.properties = properties;
        this.httpClient = createHttpClient();
    }
    
    /**
     * 创建 HttpClient，根据配置决定是否跳过 SSL 验证
     */
    private HttpClient createHttpClient() {
        HttpClient.Builder builder = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(5));
        
        // 如果配置了跳过 SSL 验证，则使用自定义的 SSL 上下文
        if (properties.isSkipSslVerification()) {
            try {
                SSLContext sslContext = createTrustAllSslContext();
                builder.sslContext(sslContext);
            } catch (Exception e) {
                throw new RuntimeException("无法创建 SSL 上下文", e);
            }
        }
        
        return builder.build();
    }
    
    /**
     * 创建一个信任所有证书的 SSL 上下文（仅用于开发/测试环境）
     */
    private SSLContext createTrustAllSslContext() throws NoSuchAlgorithmException, KeyManagementException {
        TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    // 信任所有客户端证书
                }
                
                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    // 信任所有服务器证书
                }
                
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }
        };
        
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
        return sslContext;
    }
    
    /**
     * 检查是否需要使用代理
     * 1. 检测当前位置是否为中国
     * 2. 如果是，检查代理是否可用
     */
    public boolean shouldUseProxy() {
        if (!properties.isProxyEnabled()) {
            return false;
        }
        
        // 获取当前 IP 地理位置
        IpGeoInfo geoInfo = getCurrentLocation();
        if (geoInfo == null) {
            log.debug("无法获取当前位置信息，不使用代理");
            return false;
        }
        
        // 检查是否为中国
        boolean isChina = isChina(geoInfo);
        if (!isChina) {
            log.debug("当前位置不是中国（{}），不使用代理", geoInfo.getCountry());
            return false;
        }
        
        log.info("检测到位置为中国，检查代理可用性...");
        
        // 检查代理是否可用
        return isProxyAvailable();
    }
    
    /**
     * 转换 GitHub URL 为代理 URL（使用第一个可用代理）
     */
    public String convertToProxyUrl(String url) {
        List<String> urls = getProxyUrlsWithFallback(url);
        return urls.isEmpty() ? url : urls.get(0);
    }
    
    /**
     * 获取所有可能的代理 URL（按优先级排序，最后包含原始 URL 作为兜底）
     * @param url 原始 GitHub URL
     * @return 代理 URL 列表（包括原始 URL 作为最后一个）
     */
    public List<String> getProxyUrlsWithFallback(String url) {
        if (url == null || url.isEmpty()) {
            return java.util.Collections.singletonList(url);
        }
        
        // 检查是否为 GitHub URL
        if (!isGitHubUrl(url)) {
            return java.util.Collections.singletonList(url);
        }
        
        List<String> proxyUrls = new java.util.ArrayList<>();
        List<String> proxyHosts = properties.getProxyHosts();
        
        // 为每个代理生成 URL
        for (String proxyHost : proxyHosts) {
            try {
                String proxyUrl = convertUrlWithProxy(url, proxyHost);
                if (proxyUrl != null && !proxyUrl.equals(url)) {
                    proxyUrls.add(proxyUrl);
                }
            } catch (Exception e) {
                log.debug("生成代理 URL 失败: proxy={}, error={}", proxyHost, e.getMessage());
            }
        }
        
        // 最后添加原始 URL 作为兜底
        proxyUrls.add(url);
        
        log.debug("生成 {} 个代理 URL（包含原始 URL 作为兜底）", proxyUrls.size());
        return proxyUrls;
    }
    
    /**
     * 使用指定的代理主机转换 URL
     */
    private String convertUrlWithProxy(String url, String proxyHost) {
        if (proxyHost == null || proxyHost.isEmpty()) {
            return url;
        }
        
        try {
            // 检测代理类型：如果包含 "gh-proxy" 或 "proxy" 关键字，使用前缀模式
            boolean usePrefixMode = proxyHost.contains("gh-proxy") || proxyHost.contains("proxy");
            
            String proxyUrl;
            if (usePrefixMode) {
                // 前缀模式：https://gh-proxy.com/https://github.com/user/repo/...
                String separator = proxyHost.endsWith("/") ? "" : "/";
                proxyUrl = "https://" + proxyHost + separator + url;
            } else {
                // 域名替换模式：https://gh.llkk.cc/user/repo/...
                URI uri = URI.create(url);
                String path = uri.getPath();
                String query = uri.getQuery();
                
                proxyUrl = "https://" + proxyHost + path;
                
                if (query != null && !query.isEmpty()) {
                    proxyUrl += "?" + query;
                }
            }
            
            log.debug("转换 URL: {} -> {} (代理: {}, 模式: {})", 
                    url, proxyUrl, proxyHost, usePrefixMode ? "前缀" : "替换");
            return proxyUrl;
        } catch (Exception e) {
            log.warn("URL 转换失败: url={}, proxy={}, error={}", url, proxyHost, e.getMessage());
            return url;
        }
    }
    
    /**
     * 检查是否为 GitHub URL
     */
    public boolean isGitHubUrl(String url) {
        if (url == null || url.isEmpty()) {
            return false;
        }
        return GITHUB_PATTERN.matcher(url).matches() 
            || GITHUB_API_PATTERN.matcher(url).matches();
    }
    
    /**
     * 获取当前 IP 的地理位置
     */
    private IpGeoInfo getCurrentLocation() {
        // 尝试获取公网 IP（通过多个查询服务）
        String publicIp = getPublicIp();
        
        if (publicIp == null || publicIp.isEmpty()) {
            log.warn("无法获取公网 IP，将不使用代理");
            return null;
        }
        
        log.info("已获取公网 IP: {}", publicIp);
        
        // 使用 IP 地理位置服务查询位置信息
        try {
            IpGeoInfo info = ipGeoService.getGeoInfo(publicIp);
            if (info != null) {
                log.info("检测到当前公网 IP: {}, 位置: {} ({})", 
                        publicIp, 
                        info.getCountry() != null ? info.getCountry() : "未知",
                        info.getAddress() != null ? info.getAddress() : "");
                return info;
            } else {
                log.warn("无法获取 IP {} 的地理位置信息（可能是内网 IP 或查询服务不可用）", publicIp);
            }
        } catch (Exception e) {
            log.warn("查询 IP 地理位置失败: IP={}, error={}", publicIp, e.getMessage(), e);
        }
        
        return null;
    }
    
    /**
     * 获取公网 IP 地址
     * 使用多个 IP 查询服务作为备选，提高成功率
     */
    private String getPublicIp() {
        // 多个 IP 查询服务，按优先级尝试
        String[] ipServices = {
            "https://api.ipify.org",
            "https://icanhazip.com",
            "https://ifconfig.me/ip",
            "http://checkip.amazonaws.com"
        };
        
        for (String serviceUrl : ipServices) {
            try {
                log.debug("尝试从 {} 获取公网 IP", serviceUrl);
                HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create(serviceUrl))
                        .timeout(Duration.ofSeconds(5))  // 增加超时时间
                        .GET()
                        .build();
                
                HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
                
                if (response.statusCode() == 200) {
                    String ip = response.body().trim();
                    // 验证 IP 格式（简单验证）
                    if (isValidIpFormat(ip)) {
                        log.debug("成功从 {} 获取公网 IP: {}", serviceUrl, ip);
                        return ip;
                    } else {
                        log.warn("从 {} 获取的 IP 格式无效: {}", serviceUrl, ip);
                    }
                } else {
                    log.debug("从 {} 获取 IP 失败，HTTP 状态码: {}", serviceUrl, response.statusCode());
                }
            } catch (java.net.http.HttpTimeoutException e) {
                log.debug("从 {} 获取 IP 超时: {}", serviceUrl, e.getMessage());
            } catch (Exception e) {
                log.debug("从 {} 获取 IP 失败: {}", serviceUrl, e.getMessage());
            }
        }
        
        log.warn("所有 IP 查询服务都无法获取公网 IP");
        return null;
    }
    
    /**
     * 简单验证 IP 格式（IPv4）
     */
    private boolean isValidIpFormat(String ip) {
        if (ip == null || ip.isEmpty()) {
            return false;
        }
        // 简单的 IPv4 格式验证：xxx.xxx.xxx.xxx
        return ip.matches("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
    }
    
    /**
     * 判断是否为中国
     */
    private boolean isChina(IpGeoInfo geoInfo) {
        if (geoInfo == null) {
            return false;
        }
        
        String countryCode = geoInfo.getCountryCode();
        String country = geoInfo.getCountry();
        
        return "CN".equalsIgnoreCase(countryCode)
            || "China".equalsIgnoreCase(country)
            || "中国".equals(country);
    }
    
    /**
     * 检查代理是否可用
     */
    private boolean isProxyAvailable() {
        // 使用缓存结果，避免频繁检测
        if (proxyAvailable != null) {
            return proxyAvailable;
        }
        
        String proxyHost = properties.getGithubProxy();
        
        // 根据代理类型构建测试 URL
        boolean usePrefixMode = proxyHost.contains("gh-proxy") || proxyHost.contains("proxy");
        String testUrl;
        
        if (usePrefixMode) {
            // 前缀模式：使用一个简单的 GitHub URL 测试
            testUrl = "https://" + proxyHost + "/https://github.com";
        } else {
            // 域名替换模式：直接访问代理域名
            testUrl = "https://" + proxyHost;
        }
        
        try {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(testUrl))
                    .timeout(Duration.ofSeconds(properties.getProxyCheckTimeout()))
                    .GET()
                    .build();
            
            HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());
            
            // 只要能够连接（返回 200-499 都算可用，404 说明代理服务在运行）
            boolean available = response.statusCode() < 500;
            proxyAvailable = available;
            
            if (available) {
                log.info("代理 {} 可用 (HTTP {}, 模式: {})", proxyHost, response.statusCode(), 
                        usePrefixMode ? "前缀" : "替换");
            } else {
                log.warn("代理 {} 不可用 (HTTP {})", proxyHost, response.statusCode());
            }
            
            return available;
        } catch (Exception e) {
            log.warn("代理 {} 检测失败: {}", proxyHost, e.getMessage());
            proxyAvailable = false;
            return false;
        }
    }
    
    /**
     * 重置代理可用性缓存（用于重新检测）
     */
    public void resetProxyCache() {
        proxyAvailable = null;
    }
}

